package com.jdk.demo.thread.stampedLock;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.StampedLock;

/**
 * 1、进入悲观读锁前先看下有没有进入写模式（说白了就是有没有已经获取了悲观写锁）
 * 2、如果其他线程已经获取了悲观写锁，那么就只能老老实实的获取悲观读锁（这种情况相当于退化成了读写锁）
 * 3、如果其他线程没有获取悲观写锁，那么就不用获取悲观读锁了，减少了一次获取悲观读锁的消耗和避免了因为读锁导致写锁阻塞的问题，直接返回读的数据即可（必须再tryOptimisticRead和validate之间获取好数据，否则数据可能会不一致了，试想如果过了validate再获取数据，这时数据可能被修改并且读操作也没有任何保护措施）
 * 
 * @author HP
 *
 */
public class StampedLockTest {

	static ExecutorService service = Executors.newCachedThreadPool();
	static StampedLock lock = new StampedLock();
	static int count = 0;

	public static void main(String[] args) throws InterruptedException {
		long begin = System.currentTimeMillis();
		List<Callable<Object>> list = new ArrayList<>();
		for (int i = 0; i < 1; i++) {
			list.add(() -> {
				// readLock();
				optimisticRead();
				return null;
			});
		}
		service.invokeAll(list);
		long end = System.currentTimeMillis();
		System.out.println(end - begin);
		// 乐观95 91 102 98 92
		// 悲观265 253 293 265 287
	}

	private static void readLock() {// 普通的读锁
		int currentCount = 0;
		long stamp = lock.readLock(); // 获取排他读锁
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlockRead(stamp); // 释放读锁
		}
	}

	private static void optimisticRead() {
		// 要说明的是，这三行关键代码对顺序非常敏感，不能有重排序。因为state变量已经是volatile，所以可以禁止重排序，但stamp并不是volatile的。为此，在validate(stamp)函数里面插入内存屏障
		long stamp = lock.tryOptimisticRead(); // 尝试获取乐观读锁
		int currentCount = count; // 获取变量值
		if (!lock.validate(stamp)) { // 判断count是否进入写模式
			stamp = lock.readLock(); // 已经进入写模式，没办法只能老老实实的获取读锁
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				lock.unlockRead(stamp); // 释放读锁
			}
		}
	}

}
